﻿#include "YCodec.h"

#include <QDebug>

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
}

/// <summary>
	/// 创建编解码器上下文
	/// </summary>
	/// <param name="codec_id">编解码器的id</param>
	/// <param name="is_encode">是否是编码器</param>
	/// <returns></returns>
AVCodecContext* YCodec::Create(int codec_id, bool is_encode)
{
	//找到编码器
	const AVCodec* codec = nullptr;
	if (is_encode)
	{
		codec = avcodec_find_encoder((AVCodecID)codec_id);
	}
	else {
		codec = avcodec_find_decoder((AVCodecID)codec_id);
	}
	if (!codec)
	{
		qDebug() << "avcodec_find_encoder failed!" << codec_id;
		return nullptr;
	}
	//创建编解码器上下文
	auto c = avcodec_alloc_context3(codec);
	if (!c)
	{
		qDebug() << "avcodec_alloc_context3 failed!" << codec_id;
		return nullptr;
	}
	//设置编解码器默认参数
	c->time_base = { 1,25 };
	c->pix_fmt = AV_PIX_FMT_YUV420P;
	c->thread_count = 16;
	return c;
}

/// <summary>
/// 设置编解码器上下文
/// </summary>
/// <param name="context">编解码器上下文</param>
void YCodec::setContext(AVCodecContext* context)
{
	std::unique_lock<std::mutex> lock(m_mutex);
	if (!context)
	{
		avcodec_free_context(&m_context);
		qDebug() << "编解码器上下文异常";
	}
	this->m_context = context;
}

//设置线程安全的编码参数
bool YCodec::SetOpt(const char* key, const char* val)
{
	std::unique_lock<std::mutex> lock(m_mutex);
	if (!m_context)
	{
		qDebug() << "SetOpt->编解码器上下文异常";
		return false;
	}
	auto re = av_opt_set(m_context->priv_data, key, val, 0);
	if (re != 0)
	{
		qDebug() << "av_opt_set failed!";
		PrintError(re, "av_opt_set");
	}
	return true;

}

bool YCodec::SetOpt(const char* key, int val)
{
	std::unique_lock<std::mutex>lock(m_mutex);
	if (!m_context)
	{
		qDebug() << "SetOpt->编解码器上下文异常";
		return false;
	}
	auto re = av_opt_set_int(m_context->priv_data, key, val, 0);
	if (re != 0)
	{
		qDebug() << "av_opt_set failed!";
		PrintError(re, "SetOpt");
	}
	return true;
}


/// <summary>
/// 打开编解码器
/// </summary>
/// <returns></returns>
bool YCodec::Open()
{
	std::unique_lock<std::mutex>lock(m_mutex);
	if (!m_context)
	{
		qDebug() << "Open->编解码器上下文异常";
		return false;
	}
	int ret = avcodec_open2(m_context, NULL, NULL);
	if (ret != 0)
	{
		qDebug() << "Open failed!";
		PrintError(ret, "Open");
	}
	return true;
}



/// <summary>
/// 根据AVCodecContext创建一个默认的AVFrame
/// </summary>
AVFrame* YCodec::CreateFrame()
{
	std::unique_lock<std::mutex>lock(m_mutex);
	if (!m_context)
	{
		qDebug() << "CreateFrame->编解码器上下文异常";
		return false;
	}
	auto frame = av_frame_alloc();
	frame->width = m_context->width;
	frame->height = m_context->height;
	frame->format = m_context->pix_fmt;
	auto re = av_frame_get_buffer(frame, 0);
	if (re != 0)
	{
		av_frame_free(&frame);
		PrintError(re, "CreateFrame");
		return nullptr;
	}
	return frame;
}

void YCodec::PrintError(int err, QString errMsgHead)
{
	if (err < 0)
	{
		char buf[1024] = { 0 };
		av_strerror(err, buf, sizeof(buf) - 1);
		qDebug() << errMsgHead << "->" << buf;
	}
	
}

void YCodec::Clear()
{
	std::unique_lock<std::mutex>lock(m_mutex);
	if (!m_context)return;
	avcodec_flush_buffers(m_context);//刷新缓冲区
}

YCodec::YCodec()
{
}

YCodec::~YCodec()
{
}
